package ie.timalb.chess.main.core.movement;

import ie.timalb.chess.main.core.Board;
import ie.timalb.chess.main.core.exceptions.ImpossibleToTraceARouteException;
import ie.timalb.chess.main.core.exceptions.SquareOutOfRangeException;
import ie.timalb.chess.main.core.movement.types.directions.DiagonalDirection;
import ie.timalb.chess.main.core.movement.types.directions.Direction;
import ie.timalb.chess.main.core.movement.types.directions.HorizontalDirection;
import ie.timalb.chess.main.core.movement.types.directions.VerticalDirection;
import ie.timalb.chess.main.core.movement.types.orientations.XOrientation;
import ie.timalb.chess.main.core.movement.types.orientations.XOrientationLeft;
import ie.timalb.chess.main.core.movement.types.orientations.XOrientationRight;
import ie.timalb.chess.main.core.movement.types.orientations.YOrientation;
import ie.timalb.chess.main.core.movement.types.orientations.YOrientationDown;
import ie.timalb.chess.main.core.movement.types.orientations.YOrientationUp;
import ie.timalb.chess.main.core.squares.Square;
import ie.timalb.chess.main.core.squares.SquareManager;
import ie.timalb.chess.main.core.temp.SquareIterator;
import ie.timalb.chess.main.core.temp.Squares;

import java.util.Iterator;


public class Route{
	private Square sourceSquare;
	protected Square destinySquare;	
	private int deltaX;
	int deltaY;
	private Squares squares;
	private MovementDescriptor direction;

	public Route(Square sourceSquare, Square destinySquare) 
		throws ImpossibleToTraceARouteException, SquareOutOfRangeException 
	{
		setDestinySquare(destinySquare);
		setSourceSquare(sourceSquare);		
		squares  = new Squares(sourceSquare.getContainer());		
		setDeltaY(getDestinySquare().getYCoordinate()-getSourceSquare().getYCoordinate());
		setDeltaX(getDestinySquare().getXCoordinate()-getSourceSquare().getXCoordinate());
		//This will throw an exception if it's not possible to trace a route
		setDirection(getMovemementDirection ());

		Square currentRouteSquare = getSourceSquare();
		Square nextRouteSquare = null;
		
		//We stop NOT on the destiny square BUT in the square before. The destiny square
		//will be specified by the getDestinySquare property
		Square finalRouteSquare = getSquareManager().getPreviousSquare(getDestinySquare(), getDirection().getOpposite());
		while (! currentRouteSquare.equals(finalRouteSquare))
		{
			try {
				nextRouteSquare = getSquareManager().getNextSquare(currentRouteSquare, getDirection());
				addSquare (nextRouteSquare);
				currentRouteSquare = nextRouteSquare;
			} catch (SquareOutOfRangeException e) {				
				throw new ImpossibleToTraceARouteException ();
			}
						
		}

	}
	
	public boolean isClear() {
		SquareIterator it = getIterator();
		
		while (it.hasNext())
		{
			Square next = it.next();
			if (next.isOccupied())
			{
				return false;
			}
		}
		
		return true;
	}
	
	private SquareIterator getIterator() {
		return getDirection().getIterator();
	}

	public boolean isThisSquareInRoute(Square currentRouteSquare) {
		return squares.contains(currentRouteSquare);
	}

	public int getRouteSteps () throws ImpossibleToTraceARouteException
	{
		return 0;		
	}

	private void setDeltaY(int deltaY) {
		this.deltaY = deltaY;
	}
	
	public boolean areInTheSameRow ()
	{
		boolean result = (getDeltaX() != 0) && (getDeltaY()==0);
		return result;		
	}
	
	public boolean areInTheSameColumn ()
	{
		boolean result = (getDeltaY() != 0) && (getDeltaX()==0);
		return result;		
	}
	
	public boolean areInTheSameDiagonal ()
	{
		boolean result = (getAbsDeltaY() == getAbsDeltaX());
		return result;		
	}

	private MovementDescriptor getMovemementDirection() 
		throws ImpossibleToTraceARouteException 
	{
		if ((getDeltaX()==0) && (getDeltaY()==0)) throw new ImpossibleToTraceARouteException ();
		XOrientation horizontalOrientation = null;
		YOrientation verticalOrientation = null;
		
		Direction direction = null;
		if (areInTheSameRow ())
		{
			direction = new HorizontalDirection ();
		}else if (areInTheSameColumn())
		{
			direction = new VerticalDirection ();
		}else if (areInTheSameDiagonal())
		{
			direction = new DiagonalDirection ();
		}else
		{
			throw new ImpossibleToTraceARouteException ();
		}
		
		if (getDeltaX()>0) 
		{
			horizontalOrientation = new XOrientationRight();
		}else if (getDeltaX()< 0) 
		{
			horizontalOrientation = new XOrientationLeft();
		}
		
		if (getDeltaY()>0) 
		{
			verticalOrientation = new YOrientationUp();
		}else if (getDeltaY()< 0)
		{
			verticalOrientation = new YOrientationDown();
		}
		
		return new MovementDescriptor(direction, horizontalOrientation, verticalOrientation);
	}
	
	public Square getSourceSquare() {
		return sourceSquare;
	}

	public void setSourceSquare(Square sourceSquare) 
	{
		this.sourceSquare = sourceSquare;
	}
	
	public Square getDestinySquare() {
		return destinySquare;
	}

	public void setDestinySquare(Square destinySquare) 
	{
		this.destinySquare = destinySquare;
	}

	private void addSquare(Square nextRouteSquare) {		
		squares.add(nextRouteSquare);
	}	

	public Iterator<Square> iterator() {
		Iterator<Square> it = squares.getSquareIterator();
		return it;
	}
	
	public int getDeltaX() {
		return deltaX;
	}

	private void setDeltaX(int deltaX) {
		this.deltaX = deltaX;
	}

	public int getDeltaY() {
		return deltaY;
	}
	
	public int getAbsDeltaX() {
		return Math.abs(deltaX);
	}	

	public int getAbsDeltaY() {
		return Math.abs(deltaY);
	}
	
	protected MovementDescriptor getDirection() {
		return direction;
	}

	private void setDirection(MovementDescriptor direction) {
		this.direction = direction;
	}

	private SquareManager getSquareManager() {
		return getSquares().getSquareManager();
	}

	protected Squares getSquares() {
		return squares;
	}
}